﻿using CPPEI.Coolzon.Common;
using CPPEI.Coolzon.ServiceProxy.MainServiceRef;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace CPPEI.Coolzon.ServiceProxy
{
    /// <summary>
    /// 连接服务器的代理接口的管理类
    /// </summary>
    public class ClientProxyManager
    {
        public const string EVENTBUS_FLAGNAME = "NET";

        private static ClientProxyManager s_instance = new ClientProxyManager();

        public static ClientProxyManager Instance
        {
            get
            {
                return s_instance;
            }
        }

        public ResponseKeeperV2 ResponseKeeper = new ResponseKeeperV2();

        private List<MainServiceProxy> _clientList;

        /// <summary>
        /// 获取主服务代理列表
        /// </summary>
        public List<MainServiceProxy> Proxies
        {
            get
            {
                return _clientList;
            }
        }


        private SyncServiceRef.SyncServiceClient _lastSyncClient;

        /// <summary>
        /// 最后一次发送请求的时间
        /// </summary>
        private long _latestSendTicks;
        /// <summary>
        /// 发送间隔，小于这个时间无法发送
        /// </summary>
        private long _sendInterval = TimeSpan.FromSeconds(1).Ticks;

        #region main server client

        /// <summary>
        /// 初始化客户端代理对象列表
        /// </summary>
        /// <param name="callbackImplement"></param>
        /// <returns>true:初始化之后，有连接成功,false:初始化之后，没有连接成功的</returns>
        public bool InitClientProxyList(MainServiceRef.IMainServiceCallback callbackImplement)
        {
            bool isClientOpened = false;
            if (_clientList == null)
            {
                _clientList = new List<MainServiceProxy>();
            }
            else
            {
                // 关闭已有的连接
                for (int index = 0; index < _clientList.Count; index++)
                {
                    try
                    {
                        if (_clientList[index].Client.State == CommunicationState.Opened)
                        {
                            _clientList[index].Client.Close();
                        }
                    }
                    catch
                    {
                    }
                }
                _clientList.Clear();
            }

            Configuration.Client.ServiceConfig serviceConfig = Configuration.Client.Config.Host.GetServiceConfig("MainService");

            _clientList.Add(new MainServiceProxy(serviceConfig, callbackImplement));

            if (Configuration.Client.Config.SpareHostList != null)
            {
                // 添加备用服务器信息
                for (int index = 0; index < Configuration.Client.Config.SpareHostList.Count; index++)
                {
                    Configuration.Client.ServiceConfig spareConfig = Configuration.Client.Config.SpareHostList[index].GetServiceConfig("MainService");
                    if (spareConfig != null)
                    {
                        _clientList.Add(new MainServiceProxy(spareConfig, callbackImplement));
                    }
                    else
                    {
                        Common.Logger.WarningToTag("Proxy", "备用服务器配置index:{0}，中未发现{1}的配置", index, "MainService");
                    }
                }
            }

            for (int index = 0; index < _clientList.Count; index++)
            {
                try
                {
                    _clientList[index].Client.Open();

                    _clientList[index].State = ServiceProxyState.Connected;
                    isClientOpened = true;
                }
                catch
                {
                }
            }
            return isClientOpened;
        }

        /// <summary>
        /// 不检查请求发送的时间间隔
        /// </summary>
        /// <param name="request"></param>
        public void SendRequestSys(Request request)
        {
            SendRequestCore(request);
        }
        /// <summary>
        /// 不检查请求发送的时间间隔
        /// </summary>
        /// <param name="domainId"></param>
        /// <param name="userIdentity"></param>
        /// <param name="scenario"></param>
        /// <param name="command"></param>
        /// <param name="mode"></param>
        /// <param name="requestData"></param>
        public void SendRequestSys(string domainId, string userIdentity, byte scenario, byte command, byte mode, object requestData)
        {
            Request request = RequestUtil.BuildRequest(domainId, userIdentity, scenario, command, mode, requestData);
            SendRequestSys(request);
        }

        /// <summary>
        /// 检查时间间隔
        /// </summary>
        /// <param name="request"></param>
        public void SendRequest(Request request)
        {
            long nowTicks = DateTime.Now.Ticks;

            if (nowTicks - _latestSendTicks < _sendInterval)
            {
                // 发送请求速度过快
                ModuleMessageEvent message = new ModuleMessageEvent();
                message.Module = "ServiceProxy";
                message.ErrorCode = "1";
                message.Message = "请求发送速度过快";
                EventBus.Instance.PublishAsync(message);
                Common.Logger.WarningToTag("ServiceProxy", "请求发送速度过快scenario:{0:X00}, command:{1:X00}",
                    request.Header.Scenario,
                    request.Header.Command);
                return;
            }

            try
            {
                SendRequestCore(request);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                _latestSendTicks = DateTime.Now.Ticks;
            }
        }
        public void SendRequest(string domainId, string userIdentity, byte scenario, byte command, byte mode, object requestData)
        {
            Request request = RequestUtil.BuildRequest(domainId, userIdentity, scenario, command, mode, requestData);
            SendRequest(request);
        }


        /// <summary>
        /// 实际发送指令
        /// </summary>
        /// <param name="request"></param>
        private void SendRequestCore(Request request)
        {
            List<string> serverAddressList = new List<string>();

            int expectantResponseCount = 0;
            for (int index = 0; index < _clientList.Count; index++)
            {
                if (_clientList[index].Client != null && _clientList[index].Client.State == CommunicationState.Opened)
                {
                    expectantResponseCount++;
                    serverAddressList.Add(string.Format("{0}:{1}", _clientList[index].IP, _clientList[index].Port));
                }
            }

            request.Header.ServerAddress = serverAddressList.ToArray();
            // 生成一个随机的唯一标识
            request.Header.UniqueKey = Guid.NewGuid();
            // 设置预期将收到的回应数量
            request.Header.ExpectantResponseCount = expectantResponseCount;

            Common.Logger.DebugToTag("ServiceProxy", "发送请求,scenario:0x{0:X2}, command:0x{1:X2}, uniqueKey:{2}, expectantResposeCount:{3}",
                request.Header.Scenario,
                request.Header.Command,
                request.Header.UniqueKey,
                request.Header.ExpectantResponseCount);

            ResponseKeeper.Retrive();
            ResponseKeeper.Register(request.Header.UniqueKey, request.Header.ServerAddress, expectantResponseCount);

            for (int index = 0; index < _clientList.Count; index++)
            {
                if (_clientList[index].Client != null && _clientList[index].Client.State == CommunicationState.Opened)
                {
                    try {
                        _clientList[index].Client.BeginSendRequest(request, SendRequestCallback, new object[] { _clientList[index].Client, request });
                    }
                    catch { }
                }
            }
        }
        /// <summary>
        /// 发送指令回调方法
        /// </summary>
        /// <param name="result"></param>
        private void SendRequestCallback(IAsyncResult result)
        {
            object[] obj = (object[])result.AsyncState;
            MainServiceClient mainServiceClient = (MainServiceClient)obj[0];
            Request request = (Request)obj[1];
            try
            {
                mainServiceClient.EndSendRequest(result);
            }
            catch (Exception ex)
            {
                Common.Logger.ErrorToTag("ServiceProxy", "发送请求,scenario:0x{0:X2}, command:0x{1:X2}, uniqueKey:{2}, expectantResposeCount:{3}失败,ex:{4}",
                    request.Header.Scenario,
                    request.Header.Command,
                    request.Header.UniqueKey,
                    request.Header.ExpectantResponseCount,
                    ex);
            }
        }


        /// <summary>
        /// 获得可用的服务器客户端数量
        /// </summary>
        /// <returns></returns>
        public int GetAvailableMainServiceClientCount()
        {
            int clientCount = 0;
            for (int index = 0; index < _clientList.Count; index++)
            {
                if (_clientList[index].Client.State == CommunicationState.Opened)
                {
                    clientCount++;
                }
            }
            return clientCount;
        }

        #endregion

        #region data service
        private List<HostState> _dataServiceProxies;
        private object _lockDataServiceHostObj = new object();

        private static DataServiceRef.DataServiceClient CreateDataServiceClient(Configuration.Client.HostConfig hostConfig)
        {
            Configuration.Client.ServiceConfig serviceConfig = hostConfig.GetServiceConfig("DataService");
            System.ServiceModel.Channels.Binding tcpBinding = new NetTcpBinding();
            (tcpBinding as NetTcpBinding).Security.Mode = SecurityMode.None;
            (tcpBinding as NetTcpBinding).ReceiveTimeout = TimeSpan.FromSeconds(300);
            (tcpBinding as NetTcpBinding).SendTimeout = TimeSpan.FromSeconds(300);
            (tcpBinding as NetTcpBinding).MaxBufferSize = 1024 * 1024 * 1024;
            (tcpBinding as NetTcpBinding).MaxReceivedMessageSize = 1024 * 1024 * 1024;
            EndpointAddress address = new EndpointAddress(string.Format("net.tcp://{0}:{1}/{2}/", serviceConfig.IP, serviceConfig.Port, serviceConfig.Path));

            DataServiceRef.DataServiceClient dataServiceClient = new DataServiceRef.DataServiceClient(tcpBinding, address);
            return dataServiceClient;
        }

        public delegate void GetDataCallback(ServiceProxy.DataServiceRef.DataResult dataResult, bool isSuccess = true, bool isFromCacheFile = false);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="userIdentity"></param>
        /// <param name="datasource"></param>
        /// <param name="dataset"></param>
        /// <param name="parameters"></param>
        /// <param name="callback">返回请求服务器的数量</param>
        /// <returns></returns>
        public int GetData(string userIdentity, string datasource, string dataset, CPPEI.Coolzon.ServiceProxy.DataServiceRef.DataParameter[] parameters, GetDataCallback callback)
        {
            //创建主备服务器列表
            if (_dataServiceProxies == null)
            {
                lock (_lockDataServiceHostObj)
                {
                    if (_dataServiceProxies == null)
                    {
                        _dataServiceProxies = new List<HostState>();
                        _dataServiceProxies.Add(new HostState(Configuration.Client.Config.Host, true));

                        if(Configuration.Client.Config.SpareHostList != null)
                        {
                            for (int index = 0; index < Configuration.Client.Config.SpareHostList.Count; index++)
                            {
                                _dataServiceProxies.Add(new HostState(Configuration.Client.Config.SpareHostList[index], true));
                            }
                        }
                    }
                }
            }

            int requestCount = 0;
            //遍历服务器列表,异步获取数据
            foreach (HostState hostState in _dataServiceProxies)
            {
                try
                {
                    DataServiceRef.DataServiceClient dataServiceClient = CreateDataServiceClient(hostState.Host);
                    IAsyncResult iar = dataServiceClient.BeginGetData(userIdentity, datasource, dataset, parameters, OnGetDataCallback, new object[] { dataServiceClient, callback });
                    requestCount++;
                }
                catch
                {
                    hostState.IsOK = false;
                }
            }
            return requestCount;
        }
        void OnGetDataCallback(IAsyncResult result)
        {
            if (result.IsCompleted)
            {
                object[] objs = (object[])result.AsyncState;
                DataServiceRef.DataServiceClient dataServiceClient = objs[0] as DataServiceRef.DataServiceClient;
                try
                {
                    DataServiceRef.DataResult dataResult = dataServiceClient.EndGetData(result);
                    (objs[1] as GetDataCallback).Invoke(dataResult);
                }
                catch(EndpointNotFoundException enfe)
                {
                    //Common.Logger.ErrorToTag("GetDataCallback", "发生EndpointNotFoundException:{0}", enfe.Message);
                    (objs[1] as GetDataCallback).Invoke(null, false);
                }
                catch (CommunicationException ce)
                {
                    Common.Logger.ErrorToTag("GetDataCallback", "发生CommunicationException:{0}", ce.Message);
                    (objs[1] as GetDataCallback).Invoke(null, false);
                }
                catch (Exception ex)
                {
                    Common.Logger.ErrorToTag("GetDataCallback", "发生其他异常:{0}", ex.Message);
                    (objs[1] as GetDataCallback).Invoke(null, false);
                }
                try
                {
                    if(dataServiceClient.State == CommunicationState.Opened)
                    {
                        dataServiceClient.Close();
                    }
                }
                catch
                {

                }
                finally
                {
                    dataServiceClient = null;
                }
            }
        }
        #endregion


        #region sync service
        private List<HostState> _syncServiceProxies;
        private object _lockSyncServiceHostObj = new object();

        private static SyncServiceRef.SyncServiceClient CreateSyncServiceClient(Configuration.Client.HostConfig hostConfig)
        {
            Configuration.Client.ServiceConfig serviceConfig = hostConfig.GetServiceConfig("SyncService");
            System.ServiceModel.Channels.Binding tcpBinding = new NetTcpBinding();
            (tcpBinding as NetTcpBinding).ReceiveTimeout = TimeSpan.FromSeconds(300);
            (tcpBinding as NetTcpBinding).SendTimeout = TimeSpan.FromSeconds(300);
            (tcpBinding as NetTcpBinding).Security.Mode = SecurityMode.None;
            (tcpBinding as NetTcpBinding).MaxBufferSize = 1024 * 1024 * 1024;
            (tcpBinding as NetTcpBinding).MaxReceivedMessageSize = 1024 * 1024 * 1024;
            EndpointAddress address = new EndpointAddress(string.Format("net.tcp://{0}:{1}/{2}/", serviceConfig.IP, serviceConfig.Port, serviceConfig.Path));

            SyncServiceRef.SyncServiceClient syncServiceClient = new SyncServiceRef.SyncServiceClient(tcpBinding, address);
            return syncServiceClient;
        }

        [Obsolete("废掉")]
        public SyncServiceRef.SyncServiceClient GetAvariableSyncServiceClient()
        {
            if (_syncServiceProxies == null)
            {
                lock (_lockSyncServiceHostObj)
                {
                    if (_syncServiceProxies == null)
                    {
                        _syncServiceProxies = new List<HostState>();
                        _syncServiceProxies.Add(new HostState(Configuration.Client.Config.Host, true));

                        for (int index = 0; index < Configuration.Client.Config.SpareHostList.Count; index++)
                        {
                            _syncServiceProxies.Add(new HostState(Configuration.Client.Config.SpareHostList[index], true));
                        }
                    }
                }
            }

            SyncServiceRef.SyncServiceClient client = null;
            for (int index = 0; index < _syncServiceProxies.Count; index++)
            {
                HostState hostState = _syncServiceProxies[index];
                if (hostState.IsOK)
                {
                    try
                    {
                        client = CreateSyncServiceClient(hostState.Host);
                        client.Open();
                    }
                    catch
                    {
                        client = null;
                        hostState.IsOK = false;
                    }

                    if (client != null)
                    {
                        break;
                    }
                }
            }
            return client;
        }

        public SyncServiceRef.SyncServiceClient GetAvariableSyncServiceClient2()
        {
            if (_lastSyncClient != null)
            {
                // 关闭已有的连接
                try
                {
                    if (_lastSyncClient.State == CommunicationState.Opened)
                    {
                        _lastSyncClient.Close();
                    }
                }
                catch
                {
                }
            }

            List<Configuration.Client.HostConfig> hostConfigList = new List<Configuration.Client.HostConfig>();

            hostConfigList.Add(Configuration.Client.Config.Host);

            if (Configuration.Client.Config.SpareHostList != null)
            {
                for (int index = 0; index < Configuration.Client.Config.SpareHostList.Count; index++)
                {
                    hostConfigList.Add(Configuration.Client.Config.SpareHostList[index]);
                }
            }

            SyncServiceRef.SyncServiceClient client = null;
            for (int index = 0; index < hostConfigList.Count; index++)
            {
                try
                {
                    client = CreateSyncServiceClient(hostConfigList[index]);
                    client.Open();
                }
                catch (Exception ex)
                {
                    client = null;
                    Common.Logger.ErrorToTag("ServiceProxy", "client open{0}", ex);
                }

                if (client != null)
                {
                    _lastSyncClient = client;
                    break;
                }
            }
            return client;
        }
        #endregion

        #region update service
        private List<HostState> _updateServiceProxies;
        private object _lockUpdateServiceHostObj = new object();

        private static UpdateServiceRef.UpdateServiceClient CreateUpdateServiceClient(Configuration.Client.HostConfig hostConfig)
        {
            Configuration.Client.ServiceConfig serviceConfig = hostConfig.GetServiceConfig("UpdateService");
            System.ServiceModel.Channels.Binding tcpBinding = new NetTcpBinding();
            (tcpBinding as NetTcpBinding).Security.Mode = SecurityMode.None;
            (tcpBinding as NetTcpBinding).MaxBufferSize = 1024 * 1024 * 1024;
            (tcpBinding as NetTcpBinding).MaxReceivedMessageSize = 1024 * 1024 * 1024;
            EndpointAddress address = new EndpointAddress(string.Format("net.tcp://{0}:{1}/{2}/", serviceConfig.IP, serviceConfig.Port, serviceConfig.Path));

            UpdateServiceRef.UpdateServiceClient updateServiceClient = new UpdateServiceRef.UpdateServiceClient(tcpBinding, address);
            return updateServiceClient;
        }

        public UpdateServiceRef.UpdateServiceClient GetAvariableUpdateServiceClient()
        {
            if (_updateServiceProxies == null)
            {
                lock (_lockUpdateServiceHostObj)
                {
                    if (_updateServiceProxies == null)
                    {
                        _updateServiceProxies = new List<HostState>();
                        _updateServiceProxies.Add(new HostState(Configuration.Client.Config.Host, true));

                        for (int index = 0; index < Configuration.Client.Config.SpareHostList.Count; index++)
                        {
                            _updateServiceProxies.Add(new HostState(Configuration.Client.Config.SpareHostList[index], true));
                        }
                    }
                }
            }

            UpdateServiceRef.UpdateServiceClient client = null;
            for (int index = 0; index < _updateServiceProxies.Count; index++)
            {
                HostState hostState = _updateServiceProxies[index];
                if (hostState.IsOK)
                {
                    try
                    {
                        client = CreateUpdateServiceClient(hostState.Host);
                        client.Open();
                    }
                    catch
                    {
                        client = null;
                        hostState.IsOK = false;
                    }

                    if (client != null)
                    {
                        break;
                    }
                }
            }
            return client;
        }
        #endregion
    }

    class HostState
    {
        public Configuration.Client.HostConfig Host { get; set; }
        public bool IsOK { get; set; }

        public HostState(Configuration.Client.HostConfig host, bool isOK)
        {
            this.Host = host;
            this.IsOK = isOK;
        }
    }
}
